package com.service.business.base.service.impl;


import com.service.business.base.constants.RedisKeyConstants;
import com.service.business.base.controller.req.EmailVerifyCodeReq;
import com.service.business.base.controller.req.PhoneVerifyCodeReq;
import com.service.business.base.controller.req.VerifyCodeReq;
import com.service.business.base.dto.LoginUserSession;
import com.service.business.base.enums.NotifyCodeEnum;
import com.service.business.base.enums.VerifyCodeEnum;
import com.service.business.base.service.ConfigService;
import com.service.business.base.service.VerifyCodeService;
import com.service.business.member.controller.req.TwoFaReq;
import com.service.business.message.JuHeMobileTemplatesEnum;
import com.service.business.message.MessageSender;
import com.service.business.message.MessageTemplates;
import com.service.business.support.SessionUtil;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.query.VerifyCodeParams;
import com.service.dao.mapper.SysConfigMapper;
import com.service.dao.mapper.SysVerifyCodeMapper;
import com.service.business.support.Language;
import com.service.business.utils.DateUtil;
import com.service.business.support.MemCache;
import com.service.core.exception.CommonException;
import com.service.dao.model.SysVerifyCode;
import com.service.business.email.MailService;
import com.service.utility.common.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.concurrent.ExecutorService;

/**
 * <dl>
 * <dt>platform</dt>
 * <dd>Description:</dd>
 * <dd>Copyright: Copyright (C) 2018</dd>
 * <dd>Company: </dd>
 * <dd>CreateDate: 2019-04-14</dd>
 * </dl>
 */
@Slf4j
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class VerifyCodeServiceImpl implements VerifyCodeService {

    @Autowired
    private MemCache memCache;

    @Autowired
    private SysVerifyCodeMapper verifyCodeMapper;

    @Autowired
    private SysConfigMapper sysConfigMapper;

    @Autowired
    private ConfigService configService;

    @Autowired
    ExecutorService executorService;

    @Autowired
    private SessionUtil sessionUtil;

    @Autowired
    private MailService mailService;

    @Autowired
    private MessageSender messageSender;

    private Integer sendSmsTime = 120;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void send(PhoneVerifyCodeReq verifyCodeReq) {

        log.info("账号<{}:ip:{}>获取验证码", verifyCodeReq.getAccount(), verifyCodeReq.getIpAdd());
        this.isSendVerifyCodeInLimitTime(verifyCodeReq, VerifyCodeEnum.MOBILE_PHONE);

        String code = RandomUtil.randomNumberString(6);
        log.info("账号<{}:ip:{}>获取验证码,验证码:{}", verifyCodeReq.getAccount(), verifyCodeReq.getIpAdd(), code);

//        SysConfig sysConfig = configService.getConfigByName(ConfigConstants.SMS_VERIFY_CODE_TPL_ID);
//        if (ObjectUtils.isEmpty(sysConfig)) {
//            return;
//        }
        //取消juhe接口，不存在 tpid了。
        messageSender.send(verifyCodeReq.getAccount(), verifyCodeReq.getPhoneCode(),
                code, JuHeMobileTemplatesEnum.common.getTpId());
        insertSendVerifyCodeRecord(verifyCodeReq, code, VerifyCodeEnum.MOBILE_PHONE);
    }

    private void isSendVerifyCodeInLimitTime(VerifyCodeReq verifyCodeReq, VerifyCodeEnum verifyCodeEnum) {

        VerifyCodeParams params = new VerifyCodeParams();
        params.setCodeType(verifyCodeReq.getCodeType().getValue());
        params.setSendType(verifyCodeEnum.getValue());
        params.setAccount(verifyCodeReq.getAccount());
        params.setIpAdd(verifyCodeReq.getIpAdd());
        SysVerifyCode sysVerifyCode = verifyCodeMapper.selectLastSendRecord(params);
        if (!ObjectUtils.isEmpty(sysVerifyCode)) {
            if (DateUtil.getNowSec() - sysVerifyCode.getSendTime().getTime() / 1000 <= sendSmsTime) {
                log.info("账号<{}:ip:{}>获取验证码, 发送失败，{}秒内发送多次", verifyCodeReq.getAccount(), verifyCodeReq.getIpAdd(), sendSmsTime);
                throw new CommonException("两分钟内禁止重复发送", "two_min_cont_send");
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendEmail(EmailVerifyCodeReq verifyCodeReq) {
        log.info("账号<{}:ip:{}>获取验证码", verifyCodeReq.getAccount(), verifyCodeReq.getIpAdd());
        this.isSendVerifyCodeInLimitTime(verifyCodeReq, VerifyCodeEnum.EMAIL);

        String code = RandomUtil.randomNumberString(6);
        log.info("账号<{}:ip:{}>获取验证码,验证码:{}", verifyCodeReq.getAccount(), verifyCodeReq.getIpAdd(), code);

        executorService.execute(() -> mailService.sendTplEmail(verifyCodeReq, code));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertSendVerifyCodeRecord(VerifyCodeReq verifyCodeReq, String code, VerifyCodeEnum verifyCodeEnum) {
        try{
            SysVerifyCode sysVerifyCode = new SysVerifyCode();
            sysVerifyCode.setSendIp(verifyCodeReq.getIpAdd());
            sysVerifyCode.setSendAccount(verifyCodeReq.getAccount());
            sysVerifyCode.setSendType(verifyCodeEnum.getValue());
            sysVerifyCode.setCodeType(verifyCodeReq.getCodeType().getValue());
            sysVerifyCode.setVerifyCode(code);
            sysVerifyCode.setSendTime(new Date());
            verifyCodeMapper.insertSelective(sysVerifyCode);
            memCache.set(RedisKeyConstants.VERIFY_CODE + verifyCodeReq.getCodeType().getValue() + verifyCodeReq.getAccount(), code, 1800);
        }catch (Exception e){
            log.info("保持短信记录失败");
            e.printStackTrace();
        }
    }

    @Override
    public void insertSendNotifyRecord(VerifyCodeReq verifyCodeReq, String typeName, VerifyCodeEnum verifyCodeEnum) {
        try{
            SysVerifyCode sysVerifyCode = new SysVerifyCode();
            sysVerifyCode.setSendIp(verifyCodeReq.getIpAdd());
            sysVerifyCode.setSendAccount(verifyCodeReq.getAccount());
            sysVerifyCode.setSendType(verifyCodeEnum.getValue());
            sysVerifyCode.setCodeType(verifyCodeReq.getCodeType().getValue());
            sysVerifyCode.setVerifyCode(typeName);
            sysVerifyCode.setSendTime(new Date());
            verifyCodeMapper.insertSelective(sysVerifyCode);
            memCache.setNoExpire(RedisKeyConstants.NOTIFY_MSG + typeName + verifyCodeReq.getAccount() , typeName);
        }catch (Exception e){
            log.info("保持短信记录失败");
            e.printStackTrace();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void commonSend(VerifyCodeReq verifyCodeReq){
        LoginUserSession loginUserSession = sessionUtil.getUser();
        log.info("账号<{}:ip:{}>获取验证码", loginUserSession.getAccount(), verifyCodeReq.getIpAdd());
        verifyCodeReq.setAccount(loginUserSession.getAccount());

        VerifyCodeEnum verifyCodeEnum;
        if(loginUserSession.getAccount().contains("@")){
            //邮箱
            verifyCodeEnum = VerifyCodeEnum.EMAIL;
        }else{
            //手机
            verifyCodeEnum = VerifyCodeEnum.MOBILE_PHONE;
        }
        this.isSendVerifyCodeInLimitTime(verifyCodeReq,verifyCodeEnum);
        String code = RandomUtil.randomNumberString(6);
        if(loginUserSession.getAccount().contains("@")) {
            //邮箱
            executorService.execute(() -> mailService.sendCodeTplEmail(verifyCodeReq, code));
        }else{
            //手机
            messageSender.send(verifyCodeReq.getAccount(), loginUserSession.getPhoneCode(),
                    MessageTemplates.verifyCodeMsg(loginUserSession.getPhoneCode(),code, Language.LANG.get()),null);
            insertSendVerifyCodeRecord(verifyCodeReq, code, verifyCodeEnum);
        }
    }
}
